package cn.tedu.csmall.product.filter;

import cn.tedu.csmall.product.security.LoginPrincipal;
import cn.tedu.csmall.product.web.JsonResult;
import cn.tedu.csmall.product.web.ServiceCode;
import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * <p>处理JWT的过滤器类</p>
 *
 * <p>此类的主要职责</p>
 * <ul>
 *     <li>接收客户端可能提交的JWT</li>
 *     <li>尝试解析客户端提交的JWT</li>
 *     <li>将解析得到的结果存入到SecurityContext中</li>
 * </ul>
 *
 * @author Chen
 * @since 2023/2/8 15:40:40
 */
@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    public static final int JWT_MIN_LENGTH = 113;

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    @NotNull HttpServletResponse response,
                                    @NotNull FilterChain filterChain)
            throws ServletException, IOException {
        // 清除SecurityContext中的数据
        SecurityContextHolder.clearContext();

        // 根据业内惯用的做法，客户端应该将JWT保存在请求头（Request Header）中的名为Authorization的属性中
        String jwt = request.getHeader("Authorization");
        // log.debug("尝试接收客户端携带的JWT数据，JWT：{}", jwt);

        // 若没有携带jwt
        if (!StringUtils.hasText(jwt) || jwt.length() < JWT_MIN_LENGTH) {
            // 直接放行
            filterChain.doFilter(request, response);
            return;
        }

        // 尝试解析JWT
        Claims claims;
        response.setContentType("application/json; charset=utf-8");
        try {
            claims = Jwts.parserBuilder()
                    .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes()))
                    .build()
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (ExpiredJwtException e) {
            String message = "您的登录信息已过期，请重新登录！";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        } catch (MalformedJwtException e) {
            String message = "非法访问";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        } catch (SignatureException e) {
            String message = "非法访问";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.println(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        }
        Long id = claims.get("id", Long.class);
        String username = claims.get("username", String.class);
        String authoritiesJsonString = claims.get("authoritiesJsonString", String.class);
        log.debug("从JWT中解析得到的管理员ID：{}", id);
        log.debug("从JWT中解析得到的管理员用户名：{}", username);
        log.debug("从JWT中解析得到的管理员权限列表JSON：{}", authoritiesJsonString);

        // 解析JWT的结果创建认证信息
        LoginPrincipal principal = new LoginPrincipal();
        principal.setId(id);
        principal.setUsername(username);

        List<SimpleGrantedAuthority> authorities = JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);

        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, null, authorities);

        // 将认证信息存入SecurityContext
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);

        // 过滤器链继承向后执行，即：放行
        // 如果没有以下方法，表示“阻止”
        filterChain.doFilter(request, response);
    }

}
